package com.hilatest.wsexample.basictest.chapter6;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.security.KeyManagementException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.TrustManagerFactory;
import javax.xml.ws.http.HTTPException;

import com.sun.net.httpserver.HttpContext;
import com.sun.net.httpserver.HttpExchange;
import com.sun.net.httpserver.HttpHandler;
import com.sun.net.httpserver.HttpsConfigurator;
import com.sun.net.httpserver.HttpsParameters;
import com.sun.net.httpserver.HttpsServer;

public class PublishSSLServer {

	private Map<Integer,Integer> fibs;
	
	public PublishSSLServer(){
		fibs= Collections.synchronizedMap(new HashMap<Integer,Integer>());
	}
	
	/**
	 * @return the fibs
	 */
	public Map<Integer, Integer> getFibs() {
		return fibs;
	}

	/**
	 * @param fibs the fibs to set
	 */
	public void setFibs(Map<Integer, Integer> fibs) {
		this.fibs = fibs;
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		new PublishSSLServer().publish();

	}

	private void publish(){
		int port = 10002;
		String ip="https://localhost:";
		String path = "/fib";
		String url = ip+port+path;
		
		HttpsServer server = getHttpsServer(ip,port,path);
		@SuppressWarnings("unused")
		HttpContext http_ctx = server.createContext(path);
		
		System.out.print("publishing RabbitCount at"+url);
		if(server!=null){
			server.start();
		}
	}
	private HttpsServer getHttpsServer(String ip, int port, String path) {
		HttpsServer server = null;
	
		try {
			InetSocketAddress inet = new InetSocketAddress(port);
			
			//2nd arg = max number of client requests to queue
			server = HttpsServer.create(inet, 2);
			
			SSLContext ssl_ctx = SSLContext.getInstance("TLS");
			//password for keystore
			char[] password = "qubits".toCharArray();
			KeyStore ks = KeyStore.getInstance("JKS");
			FileInputStream fis = new FileInputStream("rc.keystore");
			ks.load(fis,password);
			
			KeyManagerFactory kmf =KeyManagerFactory.getInstance("SunX509");
			kmf.init(ks, password);
			
			TrustManagerFactory tmf = TrustManagerFactory.getInstance("SunX509");
			tmf.init(ks);
			
			ssl_ctx.init(kmf.getKeyManagers(), tmf.getTrustManagers(), null);
			
			//Create SSl engine and configure HTTPS to ues it
			final SSLEngine eng = ssl_ctx.createSSLEngine();
			server.setHttpsConfigurator(new HttpsConfigurator(ssl_ctx){
				public void configure(HttpsParameters parms){
					parms.setCipherSuites(eng.getEnabledCipherSuites());
					parms.setProtocols(eng.getEnabledProtocols());
				}
			});
			
			server.setExecutor(null);  //use default
			@SuppressWarnings("unused")
			HttpContext http_context = server.createContext(path,new MyHttpHandler(this));
		} catch (IOException e) {
			e.printStackTrace();
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (KeyStoreException e) {
			e.printStackTrace();
		} catch (CertificateException e) {
			e.printStackTrace();
		} catch (UnrecoverableKeyException e) {
			e.printStackTrace();
		} catch (KeyManagementException e) {
			e.printStackTrace();
		}
		
		return server;
	}

	class MyHttpHandler implements HttpHandler{

		private PublishSSLServer pub;
		public MyHttpHandler(PublishSSLServer pub){this.pub= pub;}
		@Override
		public void handle(HttpExchange ex) throws IOException {
			String verb = ex.getRequestMethod().toUpperCase();
			if(verb.equals("GET")){
				doGet(ex);
			}else if(verb.equals("POST")){
				doPost(ex);
			}else if(verb.equals("DELETE")){
				doDelete(ex);
			}else{
				throw new HTTPException(405);
			}
			
		}
		
		private void respondToClient(HttpExchange ex,String res){
			
			try {
				ex.sendResponseHeaders(200, 0);
				OutputStream out = ex.getResponseBody();
				out.write(res.getBytes());
				out.flush();
				ex.close();
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		
		private void doDelete(HttpExchange ex) {
			Map<Integer,Integer> fibs = pub.getFibs();
			Collection<Integer> list =fibs.values();
			this.respondToClient(ex, list.toString());
			
		}
		
		private void doPost(HttpExchange ex) {
			Map<Integer,Integer> fibs = pub.getFibs();
			fibs.clear();
			try {
				InputStream in = ex.getRequestBody();
				byte[] raw_bytes = new byte[4096];
				in.read(raw_bytes);
				String nums = new String(raw_bytes);
				nums = nums.replace('[', '\0');
				nums = nums.replace(']', '\0');
				String[] parts = nums.split(",");
				List<Integer> list  = new ArrayList<Integer>();
				
				for(String next:parts){
					int n = Integer.parseInt(next.trim());
					fibs.put(n, countRabbits(n));
					list.add(fibs.get(n));
				}
				Collection<Integer> coll = fibs.values();
				String res = "POSTed"+coll.toString();
				this.respondToClient(ex, res);
			} catch (IOException e) {
					
					e.printStackTrace();
			}
			
		}
		private Integer countRabbits(int n) {
			n = Math.abs(n);
			if(n<2) return n;
			
			Map<Integer,Integer> fibs = pub.getFibs();
			
			if(fibs.containsKey(n))return fibs.get(n);
			
			if(fibs.containsKey(n-1)&&fibs.containsKey(n-2)){
				fibs.put(n, fibs.get(n-1)+fibs.get(n-2));
				return fibs.get(n);
			}
			
			int fib =1,prev =0;
			for(int i= 2;i<n;i++){
				int temp = fib;
				fib += prev;
				prev = temp;
			}
			fibs.put(n, fib);
			return fib;
		}
		private void doGet(HttpExchange ex) {
			Map<Integer,Integer> fibs = pub.getFibs();
			fibs.clear();
			this.respondToClient(ex, "All entries deleted");
			
		}
		
	}
}
